#include "Socket.h"

Socket::Socket() { }

Socket::Socket(Socket *s) {
	this->socketId = s->getRemoteSocket();
}

Socket::Socket(int puerto) {
	/* se crea el socket */
	socketId = socket(AF_INET, SOCK_STREAM, 0);
	if (socketId == -1) {
		throw 1;
	}
	
	/* Se establece la estructura localDir para luego llamar a bind() */
	localDir.sin_family = AF_INET; /* usa host byte order */
	localDir.sin_port = htons(puerto); /* usa network byte order */
	localDir.sin_addr.s_addr = INADDR_ANY; /* escuchamos en todas las IPs */
	bzero(&(localDir.sin_zero), 8); /* rellena con ceros el resto de la 
									estructura */

	/* Se le da un nombre al socket (se lo asocia al puerto e IPs) */
	int ret;
	ret = bind(socketId, (struct sockaddr *)&localDir, 
													sizeof(struct sockaddr));
	if (ret == -1) {
		throw 1;
	}
	
	/* escuchar conexiones */
	ret = listen(socketId, BACKLOG);
	if (ret == -1) {
		throw 1;
	}
}

Socket::Socket(string host, int puerto) {
	/* convertimos el hostname a su direccion IP */
	he = gethostbyname(host.c_str());
	if (he == NULL) {
		throw 504;
	}

	/* Creamos el socket */
	socketId = socket(AF_INET, SOCK_STREAM, 0);
	if (socketId == -1) {
		throw 504;
	}

	/* a donde mandar */
	remoteDir.sin_family = AF_INET; /* usa host byte order */
	remoteDir.sin_port = htons(puerto); /* usa network byte order */
	remoteDir.sin_addr = *((struct in_addr *)he->h_addr);
	bzero(&(remoteDir.sin_zero), 8); /* pone en cero el resto */

	int ret = connect(socketId, (struct sockaddr *)&remoteDir, 
															sizeof(sockaddr));

	if (ret < 0) {
		throw 504;
	}
}

Socket::~Socket() { 
	shutdown(socketId, SHUT_RDWR);
	close(socketId);
}

void Socket::enviar(string msg) {
	int byteCount;
	int porEnviar = msg.size();
	string *msgEnviar = new string(msg);

	while (porEnviar > 0) {
		byteCount = send(socketId, msgEnviar->c_str(), msgEnviar->size(), 0);
		porEnviar -= byteCount;
		if (porEnviar > 0) {
			msgEnviar->assign(msg.substr(msg.size()-porEnviar -1, msg.size()));
		}
	}
	msg.clear();
	delete(msgEnviar);
}

string Socket::recibir() {
	char buf[BUFFER_LEN];
	short byteCount = 0;
	short bytesRestantes = 3;
	while (bytesRestantes != 0){
		byteCount = recv(socketId, buf, bytesRestantes, 0);
		if (byteCount == 0) throw 2;
		bytesRestantes -= byteCount;
	}
	
	short shortsRestantes = 0;
	short i = 1;
	char * paux = (char*)&shortsRestantes;
	*paux++ = buf[i++];
	*paux = buf[i];
	
	bytesRestantes = shortsRestantes * 2;
	short bytesTotales = bytesRestantes + 3;
	byteCount = 0;
	while (bytesRestantes != 0) {
		byteCount = recv(socketId, &(buf[3]), bytesRestantes, 0);
		if (byteCount == 0) throw 2;
		bytesRestantes -= byteCount;
	}
	string ret;
	for (size_t i=0; i < bytesTotales;i++) ret+=buf[i];

	return ret;
}

Socket* Socket::aceptarConexiones() { 
	Socket* s = NULL;
	
	struct sockaddr_in dirCliente;
	unsigned int lenCli = sizeof(dirCliente);

	remoteSocket = accept(socketId, (struct sockaddr *)&dirCliente, &lenCli);
																	   		
	if (remoteSocket <= 0) {
		return NULL;
	} else {
		return new Socket(this);
	}
}

int Socket::getLocalSocket() {
	return this->localSocket;
}

int Socket::getRemoteSocket() {
	return this->remoteSocket;
}

int Socket::getSocketId() {
	return this->socketId;
}
